<template>
  <div class="simulation-container">
    <div class="simulation-content">
      <div class="simulation-controls">
        <div class="control-panel">

          <div class="control-group">
            <h3>NC代码设置</h3>
            <div class="control-item">
              <el-input type="textarea" v-model="ncCodeText" :autosize="{ minRows: 8, maxRows: 16 }"
                placeholder="在此输入或粘贴您的NC代码..." resize="vertical" style="width: 100%"></el-input>
            </div>
            <el-button type="primary" @click="updateNCCode">更新NC代码</el-button>
          </div>

          <div class="control-group">
            <h3>工件设置</h3>
            <div class="control-item">
              <label>长度 (mm)</label>
              <el-slider v-model="workpieceInfo.length" :min="50" :max="200" :step="1" show-input />
            </div>
            <div class="control-item">
              <label>宽度 (mm)</label>
              <el-slider v-model="workpieceInfo.width" :min="40" :max="150" :step="1" show-input />
            </div>
            <div class="control-item">
              <label>高度 (mm)</label>
              <el-slider v-model="workpieceInfo.height" :min="10" :max="50" :step="1" show-input />
            </div>
            <div class="control-item">
              <label>分段密度</label>
              <el-slider v-model="workpieceInfo.segmentDensity" :min="1" :max="5" :step="1" show-input />
            </div>
            <el-checkbox v-model="workpieceInfo.wireframe">线框模式</el-checkbox>
          </div>

          <div class="control-group">
            <h3>刀具设置</h3>
            <div class="control-item">
              <label>刀具半径 (mm)</label>
              <el-slider v-model="toolInfo.toolRadius" :min="3" :max="8" :step="0.5" show-input />
            </div>
            <div class="control-item">
              <label>刀具高度 (mm)</label>
              <el-slider v-model="toolInfo.toolHeight" :min="20" :max="50" :step="1" show-input />
            </div>
          </div>
        </div>

        <div class="simulation-actions">
          <el-button type="success" @click="startSimulation" :disabled="isAnimating">开始仿真</el-button>
          <el-button type="danger" @click="stopSimulation" :disabled="!isAnimating">暂停</el-button>
          <el-button type="info" @click="resetSimulation">重置</el-button>

          <div class="speed-control">
            <label>动画速度</label>
            <el-slider v-model="animationSpeed" :min="0.1" :max="5" :step="0.1" show-input />
          </div>

          <div class="view-controls">
            <el-button type="primary" @click="resetView">重置视图</el-button>
            <el-button type="primary" @click="togglePoints">显示/隐藏路径点</el-button>
            <el-button type="primary" @click="togglePath">显示/隐藏路径</el-button>
            <el-button type="primary" @click="toggleAxis">显示/隐藏坐标轴</el-button>
          </div>
        </div>
      </div>

      <div class="visualization" ref="clear"></div>
      <!-- <div class="simulation-view">
          <div class="simulation-stats">
            <div class="stat-item">
              <div class="stat-label">当前深度</div>
              <div class="stat-value">{{ currentDepth.toFixed(2) }} mm</div>
            </div>
            <div class="stat-item">
              <div class="stat-label">路径点</div>
              <div class="stat-value">{{ pointCount }}</div>
            </div>
            <div class="stat-item">
              <div class="stat-label">移动指令</div>
              <div class="stat-value">{{ moveCount }}</div>
            </div>
          </div>
        </div> -->
    </div>

    <div class="legend">
      <div class="legend-item">
        <div class="legend-color g00"></div>
        <div>G00 - 快速移动</div>
      </div>
      <div class="legend-item">
        <div class="legend-color g01"></div>
        <div>G01 - 直线进给</div>
      </div>
      <div class="legend-item">
        <div class="legend-color g02"></div>
        <div>G02 - 顺时针圆弧</div>
      </div>
      <div class="legend-item">
        <div class="legend-color g03"></div>
        <div>G03 - 逆时针圆弧</div>
      </div>
      <div class="legend-item">
        <div class="legend-color helical"></div>
        <div>螺旋下刀</div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import * as THREE from "three";
import { onMounted, ref, reactive, computed, onUnmounted, watch } from "vue";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import * as TWEEN from "three/examples/jsm/libs/tween.module.js";
import { ElSlider, ElButton, ElCheckbox } from 'element-plus';

// 响应式引用
const clear = ref<HTMLElement | null>(null);
const isAnimating = ref(false);
const currentDepth = ref(0);
const pointCount = ref(0);
const moveCount = ref(0);
const animationSpeed = ref(1);
const pointsVisible = ref(true);
const axisVisible = ref(true);

// 添加NC代码文本域绑定
const ncCodeText = ref("");

// 更新NC代码函数
function updateNCCode() {
  if (ncCodeText.value.trim()) {
    ncCode.value = ncCodeText.value;
    init();
  }
}

// 定义画布窗口宽高
const clearWindow = reactive({
  width: 1800,
  height: 1400
});

// 创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xeeeeee);

// 添加坐标轴辅助
const axesHelper = new THREE.AxesHelper(100);
scene.add(axesHelper);

// 创建渲染器
const renderer = new THREE.WebGLRenderer({
  antialias: true,
  logarithmicDepthBuffer: true,
});
renderer.setSize(clearWindow.width, clearWindow.height);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0xffffff, 1);

// 创建相机
let cameraInfo = { position: { x: -200, y: 100, z: 200 } };
const camera = new THREE.PerspectiveCamera(45, clearWindow.width / clearWindow.height, 0.000001, 200000);
camera.position.set(cameraInfo.position.x, cameraInfo.position.y, cameraInfo.position.z);
camera.lookAt(0, 0, 0);

// 轨道控制器
const controls = new OrbitControls(camera, renderer.domElement); //轨道滑动器
controls.enableDamping = true;  // 设置带阻尼的惯性
controls.dampingFactor = 0.1; // 设置阻尼系数
controls.rotateSpeed = 0.2; // 旋转速度

// 创建灯光
const bestLength = 30;
const createLight = (x: number, y: number, z: number) => {
  const light = new THREE.DirectionalLight(0xffffff, 1);
  light.position.set(x, y, z);
  light.target.position.set(0, 0, 0);
  return light;
};

const lights = [
  createLight(bestLength, bestLength, -bestLength),
  createLight(-bestLength, bestLength, -bestLength),
  createLight(-bestLength, bestLength, bestLength),
  createLight(-bestLength, -bestLength, bestLength),
  createLight(bestLength, -bestLength, -bestLength),
  createLight(-bestLength, -bestLength, -bestLength),
  createLight(-bestLength, -bestLength, bestLength),
  createLight(bestLength, -bestLength, bestLength),
];
lights.forEach(light => {
  scene.add(light);
  scene.add(light.target);
});

// 定义工件参数
const workpieceInfo = reactive({
  length: 100,
  width: 100,
  height: 25,
  segmentDensity: 3,
  heightSegments: 1,
  wireframe: false,
});

// 提升刀路高度（工件高度的一半）
const workpieceHeightOffset = computed(() => workpieceInfo.height / 2);

const workpieceSegments = computed(() => {
  return {
    lengthSegments: Math.round(workpieceInfo.length * workpieceInfo.segmentDensity),
    widthSegments: Math.round(workpieceInfo.width * workpieceInfo.segmentDensity)
  };
});

// 定义刀具参数
const toolRadius = 5; // 刀具半径 5mm
const toolHeight = 22;   // 刀具长度 22mm
let toolInfo = reactive({
  toolRadius,
  toolHeight,
  // 刀具初始位置
  position: {
    x: -toolRadius - workpieceInfo.length / 2,
    y: workpieceInfo.height / 2 + toolHeight / 2,
    z: -toolRadius - workpieceInfo.width / 2,
  }
});

// 工件和刀具对象
let workpiece: THREE.Mesh;
let tool: THREE.Mesh;

// 创建工件
function createWorkpiece() {
  const { lengthSegments, widthSegments } = workpieceSegments.value;

  const geometry = new THREE.BoxGeometry(
    workpieceInfo.length,
    workpieceInfo.height,
    workpieceInfo.width,
    lengthSegments,
    workpieceInfo.heightSegments,
    widthSegments
  );

  const material = new THREE.MeshLambertMaterial({
    color: 0xd3d3d3,
    wireframe: workpieceInfo.wireframe,
  });

  workpiece = new THREE.Mesh(geometry, material);
  workpiece.position.set(0, 0, 0);
  workpiece.receiveShadow = true;
  workpiece.castShadow = true;
  scene.add(workpiece);
  return workpiece;
}

// 创建刀具
function createTool() {
  const toolGeometry = new THREE.CylinderGeometry(
    toolInfo.toolRadius,
    toolInfo.toolRadius,
    toolInfo.toolHeight,
    32
  );

  toolGeometry.translate(0, toolInfo.toolHeight / 2, 0);

  const toolMaterial = new THREE.MeshStandardMaterial({
    color: 0x808080,
    wireframe: true
  });

  tool = new THREE.Mesh(toolGeometry, toolMaterial);
  tool.position.set(
    toolInfo.position.x,
    toolInfo.position.y,
    toolInfo.position.z
  );

  tool.castShadow = true;

  // 为刀具添加坐标系辅助对象
  // const toolAxesHelper = new THREE.AxesHelper(20);
  // tool.add(toolAxesHelper);

  scene.add(tool);
  return tool;
}

// NC代码内容
const ncCode = ref<string>(``);

// 路径点类型
interface Point {
  x: number;
  y: number;
  z: number;
  type: string;
}

let toolPathPoints: Point[] = [];
let pathVisualization: THREE.Line;
let pointCloud: THREE.Points;

// 解析NC代码
interface Position {
    X: number;
    Y: number;
    Z: number;
}

// Function to parse NC code, adapted from parseGCode
function parseNCCode(gcode: string, segmentCount: number = 64): Point[] {
    const lines = gcode.split('\n');
    const points: Point[] = [];

    let currentPosition: Position = { X: 0, Y: 0, Z: 0 };
    let currentCommand: string = 'G0';
    let currentFeedrate: number | null = null;
    let centerMode: 'absolute' | 'relative' | null = null;
    let motionMode: 'absolute' | 'incremental' = 'absolute';
    let plane: 'G17' | 'G18' | 'G19' = 'G17';
    let cycleActive = false;
    let cycleType: string | null = null;
    let cycleParams: { R: number | null; Z: number | null; Q: number | null; F: number | null; returnMode: 'G98' | 'G99' } = {
        R: null,
        Z: null,
        Q: null,
        F: null,
        returnMode: 'G98'
    };
    let initialZ: number | null = null;

    const ARC_CENTER_TOLERANCE = 0.001;
    const FULL_CIRCLE_TOLERANCE = 1e-6;
    const firstArcDetected = { used: false };

    // Helper function to add a point
    const addPoint = (command: string, x: number, y: number, z: number, feedrate: number | null, lineNumber: number) => {
        const type = command.replace(/^G(\d)$/, 'G0$1');
        points.push({ x, y: z, z: y, type }); // Map NC Z to Three.js Y, NC Y to Three.js Z
    };

    // Helper function to generate G81 drilling cycle points
    const generateG81Cycle = (x: number, y: number, lineNumber: number) => {
        if (!cycleParams.R || !cycleParams.Z || initialZ === null) return;

        // Move to hole position at initial Z
        addPoint('G00', x, y, initialZ, null, lineNumber);

        // Rapid to R plane
        addPoint('G00', x, y, cycleParams.R, null, lineNumber);

        // Feed to Z depth
        addPoint('G01', x, y, cycleParams.Z, cycleParams.F, lineNumber);

        // Return to initial Z (G98) or R plane (G99)
        const returnZ = cycleParams.returnMode === 'G98' ? initialZ : cycleParams.R;
        addPoint('G00', x, y, returnZ, null, lineNumber);
    };

    // Helper function to generate G83 peck drilling cycle points
    const generateG83Cycle = (x: number, y: number, lineNumber: number) => {
        if (!cycleParams.R || !cycleParams.Z || !cycleParams.Q || initialZ === null) return;

        // Move to hole position at initial Z
        addPoint('G00', x, y, initialZ, null, lineNumber);

        // Rapid to R plane
        addPoint('G00', x, y, cycleParams.R, null, lineNumber);

        // Peck drilling cycle
        let depth = cycleParams.R;
        while (depth > cycleParams.Z) {
            let nextDepth = depth - cycleParams.Q;
            if (nextDepth < cycleParams.Z) nextDepth = cycleParams.Z;

            // Feed to next depth
            addPoint('G01', x, y, nextDepth, cycleParams.F, lineNumber);

            // Rapid back to R plane
            if (nextDepth > cycleParams.Z) {
                addPoint('G00', x, y, cycleParams.R, null, lineNumber);
            }

            depth = nextDepth;
        }

        // Return to initial Z (G98) or R plane (G99)
        const returnZ = cycleParams.returnMode === 'G98' ? initialZ : cycleParams.R;
        addPoint('G00', x, y, returnZ, null, lineNumber);
    };

    // Add initial point
    addPoint('start', currentPosition.X, currentPosition.Y, currentPosition.Z, currentFeedrate, 0);

    for (let i = 0; i < lines.length; i++) {
        let line = lines[i].toUpperCase().replace(/;.*$/, '').trim();
        if (line === '' || line.startsWith('(') || line.startsWith('%')) continue;

        const tokens = [...line.matchAll(/([A-Z])([-+]?[0-9]*\.?[0-9]+)/g)];
        const params: { [key: string]: number[] } = {};

        for (const [, letter, value] of tokens) {
            if (!params[letter]) params[letter] = [];
            params[letter].push(parseFloat(value));
        }

        const gCodes = params['G'] || [];
        let newCommandSet = false;

        for (const g of gCodes) {
            if (g === 90.1) centerMode = 'absolute';
            else if (g === 91.1) centerMode = 'relative';
            else if (g === 90) motionMode = 'absolute';
            else if (g === 91) motionMode = 'incremental';
            else if ([0, 1, 2, 3].includes(g)) {
                currentCommand = `G${g}`;
                newCommandSet = true;
            } else if ([17, 18, 19].includes(g)) {
                plane = `G${g}` as 'G17' | 'G18' | 'G19';
            } else if (g === 81) {
                cycleActive = true;
                cycleType = 'G81';
                cycleParams.R = params['R']?.[0] ?? null;
                cycleParams.Z = params['Z']?.[0] ?? null;
                cycleParams.F = params['F']?.[0] ?? currentFeedrate;
                initialZ = currentPosition.Z;
                // Execute cycle at current position
                generateG81Cycle(currentPosition.X, currentPosition.Y, i);
            } else if (g === 83) {
                cycleActive = true;
                cycleType = 'G83';
                cycleParams.R = params['R']?.[0] ?? null;
                cycleParams.Z = params['Z']?.[0] ?? null;
                cycleParams.Q = params['Q']?.[0] ?? null;
                cycleParams.F = params['F']?.[0] ?? currentFeedrate;
                initialZ = currentPosition.Z;
                // Execute cycle at current position
                generateG83Cycle(currentPosition.X, currentPosition.Y, i);
            } else if (g === 98) {
                cycleParams.returnMode = 'G98';
            } else if (g === 99) {
                cycleParams.returnMode = 'G99';
            } else if (g === 80) {
                cycleActive = false;
                cycleType = null;
                cycleParams = { R: null, Z: null, Q: null, F: null, returnMode: 'G98' };
                initialZ = null;
            }
        }

        if (params['F']) currentFeedrate = params['F'][0];

        const x = params['X']?.[0];
        const y = params['Y']?.[0];
        const z = params['Z']?.[0];
        const iVal = params['I']?.[0] ?? 0;
        const jVal = params['J']?.[0] ?? 0;
        const kVal = params['K']?.[0] ?? 0;
        const rVal = params['R']?.[0];

        if (cycleActive && (x !== undefined || y !== undefined)) {
            // Update position for cycle
            const pos: Position = { ...currentPosition };
            if (x !== undefined) pos.X = motionMode === 'absolute' ? x : currentPosition.X + x;
            if (y !== undefined) pos.Y = motionMode === 'absolute' ? y : currentPosition.Y + y;
            currentPosition = { ...pos };
            // Generate cycle points for new position
            if (cycleType === 'G81') {
                generateG81Cycle(currentPosition.X, currentPosition.Y, i);
            } else if (cycleType === 'G83') {
                generateG83Cycle(currentPosition.X, currentPosition.Y, i);
            }
        } else if (currentCommand === 'G2' || currentCommand === 'G3') {
            const target: Position = { ...currentPosition };
            if (x !== undefined) target.X = motionMode === 'absolute' ? x : currentPosition.X + x;
            if (y !== undefined) target.Y = motionMode === 'absolute' ? y : currentPosition.Y + y;
            if (z !== undefined) target.Z = motionMode === 'absolute' ? z : currentPosition.Z + z;

            let axisA: 'X' | 'Y' | 'Z' = 'X';
            let axisB: 'X' | 'Y' | 'Z' = 'Y';
            let iKey: 'I' | 'J' | 'K' = 'I';
            let jKey: 'I' | 'J' | 'K' = 'J';
            if (plane === 'G18') {
                axisA = 'Z';
                axisB = 'X';
                iKey = 'K';
                jKey = 'I';
            } else if (plane === 'G19') {
                axisA = 'Y';
                axisB = 'Z';
                iKey = 'J';
                jKey = 'K';
            }

            const startA = currentPosition[axisA];
            const startB = currentPosition[axisB];
            const endA = target[axisA];
            const endB = target[axisB];

            let centerA: number, centerB: number;

            if (rVal !== undefined) {
                const dx = endA - startA;
                const dy = endB - startB;
                const chord2 = dx * dx + dy * dy;
                const h = Math.sqrt(Math.max(0, rVal * rVal - chord2 / 4));
                const dir = currentCommand === 'G2' ? -1 : 1;

                const mx = (startA + endA) / 2;
                const my = (startB + endB) / 2;
                const nx = -dy / Math.sqrt(chord2);
                const ny = dx / Math.sqrt(chord2);

                centerA = mx + dir * h * nx;
                centerB = my + dir * h * ny;
            } else {
                const relCenter = {
                    A: currentPosition[axisA] + (iKey === 'I' ? iVal : jKey === 'I' ? iVal : kVal),
                    B: currentPosition[axisB] + (jKey === 'J' ? jVal : iKey === 'J' ? jVal : kVal)
                };
                const absCenter = {
                    A: (iKey === 'I' ? iVal : jKey === 'I' ? iVal : kVal),
                    B: (jKey === 'J' ? jVal : iKey === 'J' ? jVal : kVal)
                };

                if (!centerMode && !firstArcDetected.used) {
                    const distRel = Math.abs(
                        Math.hypot(startA - relCenter.A, startB - relCenter.B) -
                        Math.hypot(endA - relCenter.A, endB - relCenter.B)
                    );
                    const distAbs = Math.abs(
                        Math.hypot(startA - absCenter.A, startB - absCenter.B) -
                        Math.hypot(endA - absCenter.A, endB - absCenter.B)
                    );
                    centerMode = distRel <= distAbs ? 'relative' : 'absolute';
                    firstArcDetected.used = true;
                }

                const chosen = centerMode === 'relative' ? relCenter : absCenter;
                centerA = chosen.A;
                centerB = chosen.B;
            }

            const startAngle = Math.atan2(startB - centerB, startA - centerA);
            let endAngle = Math.atan2(endB - centerB, endA - centerA);
            const radius = Math.hypot(startA - centerA, startB - centerB);

            let sweep = endAngle - startAngle;
            const isFullCircle = Math.hypot(endA - startA, endB - startB) < FULL_CIRCLE_TOLERANCE;
            if (isFullCircle) {
                sweep = currentCommand === 'G2' ? -2 * Math.PI : 2 * Math.PI;
            } else {
                if (currentCommand === 'G2' && sweep > 0) sweep -= 2 * Math.PI;
                if (currentCommand === 'G3' && sweep < 0) sweep += 2 * Math.PI;
            }

            const orthogonalAxis: 'X' | 'Y' | 'Z' = plane === 'G17' ? 'Z' : plane === 'G18' ? 'Y' : 'X';
            const dOrthogonal = target[orthogonalAxis] - currentPosition[orthogonalAxis];

            for (let j = 1; j <= segmentCount; j++) {
                const angle = startAngle + (sweep * j) / segmentCount;
                const ratio = j / segmentCount;
                const point: Position = { ...currentPosition };

                point[axisA] = centerA + radius * Math.cos(angle);
                point[axisB] = centerB + radius * Math.sin(angle);
                point[orthogonalAxis] = currentPosition[orthogonalAxis] + ratio * dOrthogonal;

                addPoint(currentCommand, point.X, point.Y, point.Z, currentFeedrate, i);
            }

            currentPosition = { ...target };
        } else if (!cycleActive && (x !== undefined || y !== undefined || z !== undefined || (gCodes.length > 0 && newCommandSet))) {
            const pos: Position = { ...currentPosition };
            if (x !== undefined) pos.X = motionMode === 'absolute' ? x : currentPosition.X + x;
            if (y !== undefined) pos.Y = motionMode === 'absolute' ? y : currentPosition.Y + y;
            if (z !== undefined) pos.Z = motionMode === 'absolute' ? z : currentPosition.Z + z;

            currentPosition = { ...pos };
            addPoint(currentCommand, pos.X, pos.Y, pos.Z, currentFeedrate, i);
        }
    }

    return points;
}
// 创建路径可视化
function createPathVisualization(points: Point[]) {
  // 移除旧的路径可视化
  if (pathVisualization) {
    scene.remove(pathVisualization);
    pathVisualization.geometry.dispose();
  }

  if (pointCloud) {
    scene.remove(pointCloud);
    pointCloud.geometry.dispose();
  }

  // 创建路径几何体
  const geometry = new THREE.BufferGeometry();
  const material = new THREE.LineBasicMaterial({
    vertexColors: true,
    linewidth: 2
  });

  const vertices: number[] = [];
  const colors: number[] = [];

  // 创建路径点
  const pointGeometry = new THREE.BufferGeometry();
  const pointMaterial = new THREE.PointsMaterial({
    color: 0xffffff,
    size: 2.5,
    sizeAttenuation: false
  });

  const pointPositions: number[] = [];

  for (let i = 0; i < points.length - 1; i++) {
    const p1 = points[i];
    const p2 = points[i + 1];

    // 添加线段
    vertices.push(p1.x, p1.y, p1.z);
    vertices.push(p2.x, p2.y, p2.z);

    // 添加点
    pointPositions.push(p1.x, p1.y, p1.z);

    // 设置颜色
    let color: THREE.Color;
    if (p2.type === 'G00') color = new THREE.Color(1, 0, 0); // 红色
    else if (p2.type === 'G01') color = new THREE.Color(0, 1, 0); // 绿色
    else if (p2.type === 'G02') color = new THREE.Color(0, 0.6, 1); // 蓝色
    else if (p2.type === 'G03') color = new THREE.Color(0.78, 0.49, 1); // 紫色
    else if (p2.type === 'G28') color = new THREE.Color(1, 0.67, 0.2); // 橙色
    else if (p2.type === 'spiral') color = new THREE.Color(1, 0.42, 0.42); // 浅红色
    else if (p2.type === 'helical') color = new THREE.Color(1, 0.82, 0.4); // 黄色
    else color = new THREE.Color(0.7, 0.7, 0.7); // 灰色

    colors.push(color.r, color.g, color.b);
    colors.push(color.r, color.g, color.b);
  }

  // 添加最后一个点
  const lastPoint = points[points.length - 1];
  pointPositions.push(lastPoint.x, lastPoint.y, lastPoint.z);

  geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
  geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));

  pathVisualization = new THREE.LineSegments(geometry, material);
  scene.add(pathVisualization);

  // 添加点
  pointGeometry.setAttribute('position', new THREE.Float32BufferAttribute(pointPositions, 3));
  pointCloud = new THREE.Points(pointGeometry, pointMaterial);
  scene.add(pointCloud);
}

// 切削算法
function processWorkpieceCut(workpiece: THREE.Mesh, tool: THREE.Mesh): boolean {
  // 1. 碰撞检测（包围盒）
  const workpieceBox = new THREE.Box3().setFromObject(workpiece);
  const toolBox = new THREE.Box3().setFromObject(tool);
  const hasCollision = workpieceBox.intersectsBox(toolBox);

  if (!hasCollision) return false;

  // 2. 转换刀具位置到工件局部坐标系
  const toolLocalPos = workpiece.worldToLocal(tool.position.clone());

  // 3. 计算工件底面高度
  const workpieceBottom = -workpieceInfo.height / 2; // 工件底面在局部坐标系中的Y值

  // 4. 获取几何数据
  const geometry = workpiece.geometry as THREE.BufferGeometry;
  const positionAttr = geometry.getAttribute('position');
  const toolBottomY = toolLocalPos.y; // 刀具底部高度

  // 5. 判断是否贯通切削
  const isThroughCut = toolBottomY <= workpieceBottom;
  let isCut = false;

  // 6. 遍历顶点处理
  for (let i = 0; i < positionAttr.count; i++) {
    const vertex = new THREE.Vector3().fromBufferAttribute(positionAttr, i);

    // 计算xz平面距离
    const dx = vertex.x - toolLocalPos.x;
    const dz = vertex.z - toolLocalPos.z;
    const distanceSq = dx * dx + dz * dz;

    // 判断是否在切割范围内
    const currentToolRadius = toolInfo.toolRadius;
    if (distanceSq <= currentToolRadius * currentToolRadius) {
      let newHeight = vertex.y;

      if (isThroughCut) {
        // 贯通切削：直接将顶点压到工件底面以下
        newHeight = workpieceBottom - 0.1; // 低于底面形成通孔
      } else if (vertex.y > toolBottomY) {
        // 非贯通切削：仅降低到刀具底部高度
        newHeight = Math.max(toolBottomY, workpieceBottom);
      }

      // 更新顶点高度
      if (vertex.y !== newHeight) {
        positionAttr.setXYZ(i, vertex.x, newHeight, vertex.z);
        isCut = true;
      }
    }
  }

  // 7. 刷新网格
  if (isCut) {
    positionAttr.needsUpdate = true;
    geometry.computeVertexNormals();
    geometry.computeBoundingBox();
    geometry.computeBoundingSphere();
  }

  return isCut;
}

// 动画相关变量
let animationIndex = 0;
let animationTween: TWEEN.Tween<any> | null = null;

// 开始仿真
function startSimulation() {
  if (isAnimating.value) return;

  isAnimating.value = true;
  animationIndex = 0;
  animateNextPoint();
}

// 动画下一路径点
function animateNextPoint() {
  if (!isAnimating.value || animationIndex >= toolPathPoints.length - 1) {
    isAnimating.value = false;
    return;
  }

  const startPoint = toolPathPoints[animationIndex];
  const endPoint = toolPathPoints[animationIndex + 1];

  // 计算两点间距离和所需时间
  const distance = Math.sqrt(
    Math.pow(endPoint.x - startPoint.x, 2) +
    Math.pow(endPoint.y - startPoint.y, 2) +
    Math.pow(endPoint.z - startPoint.z, 2)
  );

  // 使用动画速度计算持续时间
  const duration = (distance / (animationSpeed.value * 50)) * 1000; // 毫秒
  let frame : number = 0;

  // 创建Tween动画
  animationTween = new TWEEN.Tween(tool.position)
    .to({
      x: endPoint.x,
      y: endPoint.y,
      z: endPoint.z
    }, duration)
    .easing(TWEEN.Easing.Linear.None)
    .onUpdate(() => {
      // 更新当前深度
      currentDepth.value = Math.abs(tool.position.z);
      frame++;

      if(frame % 2 === 0){
          // 进行切削计算
        if (processWorkpieceCut(workpiece, tool)) {
          // 刷新工件显示
          const geometry = workpiece.geometry as THREE.BufferGeometry;
          geometry.attributes.position.needsUpdate = true;
          geometry.computeVertexNormals();
        }
      }
    })
    .onComplete(() => {
      animationIndex++;
      animateNextPoint();
    })
    .start();
}

// 暂停仿真
function stopSimulation() {
  isAnimating.value = false;
  if (animationTween) {
    animationTween.stop();
    animationTween = null;
  }
}

// 重置仿真
function resetSimulation() {
  stopSimulation();

  // 移除旧工件和刀具
  if (workpiece) scene.remove(workpiece);
  if (tool) scene.remove(tool);

  // 创建新的工件和刀具
  createWorkpiece();
  createTool();

  // 重置状态
  animationIndex = 0;
  tool.position.set(
    toolInfo.position.x,
    toolInfo.position.y,
    toolInfo.position.z
  );
  currentDepth.value = 0;
}

// 重置视图
function resetView() {
  camera.position.set(cameraInfo.position.x, cameraInfo.position.y, cameraInfo.position.z);
  camera.lookAt(0, 0, 0);
  controls.reset();
}

// 切换路径点显示
function togglePoints() {
  pointsVisible.value = !pointsVisible.value;
  if (pointCloud) pointCloud.visible = pointsVisible.value;
  // if (pathVisualization) pathVisualization.visible = pointsVisible.value;
}
// 切换路径显示
function togglePath() {
  pointsVisible.value = !pointsVisible.value;
  if (pathVisualization) pathVisualization.visible = pointsVisible.value;
}

// 切换坐标轴显示
function toggleAxis() {
  axisVisible.value = !axisVisible.value;
  axesHelper.visible = axisVisible.value;
}

// 渲染循环
function animate() {
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);
  TWEEN.update();
}

// 初始化
function init() {
  // 解析NC代码 拿到加工终点的值赋值给加工起点
  toolPathPoints = parseNCCode(ncCode.value);
  // currentX = 0;
  // currentY = 0;
  // currentZ = toolPathPoints[toolPathPoints.length - 1].y;
  toolPathPoints = parseNCCode(ncCode.value);

  // 提升整个刀路：在y方向（高度方向）提升半个工件高度
  const offsetY = workpieceHeightOffset.value;
  toolPathPoints = toolPathPoints.map(point => ({
    ...point,
    y: point.y + offsetY
  }));

  // 创建路径可视化
  createPathVisualization(toolPathPoints);

  // 渲染循环
  animate();
}

// 监视工件参数变化
watch(() => workpieceInfo, () => {
  if (workpiece) {
    // 更新工件几何
    const { lengthSegments, widthSegments } = workpieceSegments.value;

    const newGeometry = new THREE.BoxGeometry(
      workpieceInfo.length,
      workpieceInfo.height,
      workpieceInfo.width,
      lengthSegments,
      workpieceInfo.heightSegments,
      widthSegments
    );

    workpiece.geometry.dispose();
    workpiece.geometry = newGeometry;

    const material = workpiece.material as THREE.MeshLambertMaterial;
    material.wireframe = workpieceInfo.wireframe;
    material.needsUpdate = true;
  }
}, { deep: true });

// 监视刀具参数变化
watch(() => toolInfo.toolRadius, (newVal) => {
  if (tool) {
    const oldPosition = tool.position.clone();
    scene.remove(tool);

    const toolGeometry = new THREE.CylinderGeometry(
      newVal,
      newVal,
      toolInfo.toolHeight,
      32
    );
    toolGeometry.translate(0, toolInfo.toolHeight / 2, 0);

    const toolMaterial = new THREE.MeshStandardMaterial({
      color: 0x808080,
      wireframe: true
    });

    tool = new THREE.Mesh(toolGeometry, toolMaterial);
    tool.position.copy(oldPosition);

    // 重新添加坐标系
    const toolAxesHelper = new THREE.AxesHelper(20);
    tool.add(toolAxesHelper);

    scene.add(tool);
  }
});

// 组件挂载
onMounted(() => {
  if (clear.value) {
    clear.value.appendChild(renderer.domElement); // 添加渲染器到容器中
    // 创建工件和刀具
    createWorkpiece();
    createTool();
    // init();
  }
});

// 组件卸载
onUnmounted(() => {
  stopSimulation();
});
</script>

<style scoped>
.simulation-container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background: linear-gradient(135deg, #1a2a6c, #2c3e50);
  color: white;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  overflow: hidden;
}

.simulation-header {
  text-align: center;
  padding: 20px;
  background: rgba(0, 0, 0, 0.3);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.simulation-header h1 {
  margin: 0;
  font-size: 2.2rem;
  background: linear-gradient(90deg, #4facfe, #00f2fe);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.simulation-header p {
  margin: 5px 0 0;
  color: #a0d2ff;
}

.simulation-content {
  display: flex;
  flex: 1;
  padding: 20px;
  gap: 20px;
}

.simulation-controls {
  width: 300px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.control-panel {
  background: rgba(25, 40, 65, 0.85);
  border-radius: 10px;
  padding: 15px;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}

.control-group {
  margin-bottom: 20px;
}

.control-group h3 {
  margin-top: 0;
  margin-bottom: 15px;
  color: #4facfe;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  padding-bottom: 5px;
}

.control-item {
  margin-bottom: 15px;
}

.control-item label {
  display: block;
  margin-bottom: 5px;
  color: #a0d2ff;
  font-size: 0.9rem;
}

.simulation-actions {
  background: rgba(25, 40, 65, 0.85);
  border-radius: 10px;
  padding: 15px;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
  display: flex;
  flex-direction: column;
  gap: 15px;
}

.speed-control {
  background: rgba(15, 30, 55, 0.7);
  border-radius: 8px;
  padding: 10px;
}

.speed-control label {
  display: block;
  margin-bottom: 8px;
  color: #a0d2ff;
}

.view-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.simulation-view {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.visualization {
  flex: 1;
  background: #0a1424;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}

.simulation-stats {
  display: flex;
  justify-content: space-around;
  margin-top: 15px;
  background: rgba(25, 40, 65, 0.85);
  border-radius: 10px;
  padding: 15px;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}

.stat-item {
  text-align: center;
}

.stat-label {
  color: #a0d2ff;
  font-size: 0.9rem;
  margin-bottom: 5px;
}

.stat-value {
  font-size: 1.5rem;
  font-weight: bold;
  color: #00f2fe;
}

.legend {
  display: flex;
  justify-content: center;
  gap: 20px;
  padding: 15px;
  background: rgba(25, 40, 65, 0.85);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}

.legend-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.9rem;
}

.legend-color {
  width: 20px;
  height: 4px;
  border-radius: 2px;
}

.g00 {
  background: #ff4d4d;
}

.g01 {
  background: #4dff88;
}

.g02 {
  background: #4d9dff;
}

.g03 {
  background: #c77dff;
}

.helical {
  background: #ffd166;
}

/* 按钮样式 */
.el-button {
  transition: all 0.3s ease;
}

.el-button:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}

.el-button--success {
  background: linear-gradient(to right, #4CAF50, #8BC34A);
  border: none;
}

.el-button--danger {
  background: linear-gradient(to right, #F44336, #E91E63);
  border: none;
}

.el-button--info {
  background: linear-gradient(to right, #2196F3, #03A9F4);
  border: none;
}

.el-button--primary {
  background: linear-gradient(to right, #3F51B5, #2196F3);
  border: none;
}
</style>